import * as fs from 'fs/promises';
import * as path from 'path';
import * as crypto from 'crypto';
import { LocalArtifact } from './local-repository.js';
import { ClassInfo } from './types.js';
import { ClassFinder } from './class-finder.js';

export interface ClassIndexEntry {
  className: string;
  fullName: string;
  packageName: string;
  artifact: {
    groupId: string;
    artifactId: string;
    version: string;
    localPath: string;
    lastModified: number;
    size: number;
  };
}

export interface RepositoryIndexMetadata {
  version: string;
  repositoryPath: string;
  repositoryHash: string;
  generatedAt: number;
  lastScanned: number;
  totalArtifacts: number;
  totalClasses: number;
  indexFiles: string[];
}

export class StreamingIndexManager {
  private readonly INDEX_VERSION = '2.0.0';
  private readonly BATCH_SIZE = 50; // Process 50 artifacts at a time
  private readonly MAX_CLASSES_PER_FILE = 10000; // Max classes per index file
  private indexesDir: string;
  private classFinder: ClassFinder;

  constructor(classFinder: ClassFinder) {
    this.classFinder = classFinder;
    this.indexesDir = path.join(process.cwd(), '.indexes');
  }

  private async ensureIndexesDir(): Promise<void> {
    try {
      await fs.mkdir(this.indexesDir, { recursive: true });
    } catch (error) {
      console.error(`[StreamingIndexManager] 创建索引目录失败: ${error}`);
    }
  }

  private getRepositoryHash(repositoryPath: string): string {
    return crypto.createHash('md5').update(repositoryPath).digest('hex').substring(0, 8);
  }

  private getMetadataFilePath(repositoryPath: string): string {
    const repoName = path.basename(repositoryPath);
    const repoHash = this.getRepositoryHash(repositoryPath);
    return path.join(this.indexesDir, `${repoName}-${repoHash}.metadata.json`);
  }

  private getIndexFilePath(repositoryPath: string, fileIndex: number): string {
    const repoName = path.basename(repositoryPath);
    const repoHash = this.getRepositoryHash(repositoryPath);
    return path.join(this.indexesDir, `${repoName}-${repoHash}.index.${fileIndex}.json`);
  }

  async loadIndex(repositoryPath: string): Promise<Map<string, ClassIndexEntry[]> | null> {
    try {
      await this.ensureIndexesDir();
      const metadataPath = this.getMetadataFilePath(repositoryPath);
      
      console.error(`[StreamingIndexManager] 尝试加载索引元数据: ${metadataPath}`);
      
      const metadataData = await fs.readFile(metadataPath, 'utf-8');
      const metadata: RepositoryIndexMetadata = JSON.parse(metadataData);
      
      // Validate metadata
      if (metadata.version !== this.INDEX_VERSION) {
        console.error(`[StreamingIndexManager] 索引版本不匹配，需要重新生成`);
        return null;
      }
      
      if (metadata.repositoryPath !== repositoryPath) {
        console.error(`[StreamingIndexManager] 仓库路径不匹配，需要重新生成`);
        return null;
      }
      
      // Check if repository was modified
      try {
        const repoStats = await fs.stat(repositoryPath);
        if (repoStats.mtimeMs > metadata.lastScanned) {
          console.error(`[StreamingIndexManager] 仓库已修改，需要重新生成索引`);
          return null;
        }
      } catch (error) {
        console.error(`[StreamingIndexManager] 无法检查仓库状态: ${error}`);
        return null;
      }
      
      // Load index files
      const classesMap = new Map<string, ClassIndexEntry[]>();
      let totalLoaded = 0;
      
      console.error(`[StreamingIndexManager] 开始加载 ${metadata.indexFiles.length} 个索引文件...`);
      
      for (const indexFile of metadata.indexFiles) {
        try {
          const indexPath = path.join(this.indexesDir, indexFile);
          const indexData = await fs.readFile(indexPath, 'utf-8');
          const indexContent = JSON.parse(indexData);
          
          // Merge classes into the main map
          for (const [key, entries] of Object.entries(indexContent.classes || {})) {
            if (!classesMap.has(key)) {
              classesMap.set(key, []);
            }
            classesMap.get(key)!.push(...(entries as ClassIndexEntry[]));
            totalLoaded += (entries as ClassIndexEntry[]).length;
          }
          
          // Force garbage collection of temporary data
          if (global.gc) {
            global.gc();
          }
          
        } catch (error) {
          console.error(`[StreamingIndexManager] 加载索引文件失败 ${indexFile}: ${error}`);
          return null;
        }
      }
      
      console.error(`[StreamingIndexManager] 成功加载索引: ${totalLoaded} 个类条目，${metadata.totalArtifacts} 个artifact`);
      return classesMap;
      
    } catch (error) {
      console.error(`[StreamingIndexManager] 加载索引失败: ${error}`);
      return null;
    }
  }

  private async saveIndexFile(indexFilePath: string, classes: Map<string, ClassIndexEntry[]>): Promise<void> {
    const indexData = {
      classes: Object.fromEntries(classes)
    };
    
    await fs.writeFile(indexFilePath, JSON.stringify(indexData), 'utf-8');
    console.error(`[StreamingIndexManager] 保存索引文件: ${path.basename(indexFilePath)} (${classes.size} 个索引条目)`);
  }

  private async saveMetadata(metadata: RepositoryIndexMetadata): Promise<void> {
    const metadataPath = this.getMetadataFilePath(metadata.repositoryPath);
    await fs.writeFile(metadataPath, JSON.stringify(metadata, null, 2), 'utf-8');
    console.error(`[StreamingIndexManager] 保存元数据: ${path.basename(metadataPath)}`);
  }

  async generateIndex(repositoryPath: string, artifacts: LocalArtifact[]): Promise<Map<string, ClassIndexEntry[]>> {
    console.error(`[StreamingIndexManager] 开始流式生成仓库索引: ${repositoryPath}`);
    console.error(`[StreamingIndexManager] 需要处理 ${artifacts.length} 个artifact，批次大小: ${this.BATCH_SIZE}`);
    
    await this.ensureIndexesDir();
    
    const metadata: RepositoryIndexMetadata = {
      version: this.INDEX_VERSION,
      repositoryPath,
      repositoryHash: this.getRepositoryHash(repositoryPath),
      generatedAt: Date.now(),
      lastScanned: Date.now(),
      totalArtifacts: artifacts.length,
      totalClasses: 0,
      indexFiles: []
    };

    let processedCount = 0;
    let totalClassCount = 0;
    let currentIndexFile = 0;
    let currentClasses = new Map<string, ClassIndexEntry[]>();
    let currentClassCount = 0;
    
    const finalIndex = new Map<string, ClassIndexEntry[]>();

    // Process artifacts in batches
    for (let i = 0; i < artifacts.length; i += this.BATCH_SIZE) {
      const batch = artifacts.slice(i, i + this.BATCH_SIZE);
      
      console.error(`[StreamingIndexManager] 处理批次 ${Math.floor(i / this.BATCH_SIZE) + 1}/${Math.ceil(artifacts.length / this.BATCH_SIZE)} (${batch.length} 个artifact)`);
      
      for (const artifact of batch) {
        try {
          processedCount++;
          
          if (processedCount % 100 === 0) {
            console.error(`[StreamingIndexManager] 进度: ${processedCount}/${artifacts.length} (${Math.round(processedCount / artifacts.length * 100)}%)`);
          }
          
          const classes = await this.classFinder.findClassesInJar(artifact.localPath);
          
          for (const classInfo of classes) {
            const indexEntry: ClassIndexEntry = {
              className: classInfo.className,
              fullName: classInfo.fullName,
              packageName: classInfo.packageName,
              artifact: {
                groupId: artifact.groupId,
                artifactId: artifact.artifactId,
                version: artifact.version,
                localPath: artifact.localPath,
                lastModified: artifact.lastModified.getTime(),
                size: artifact.size
              }
            };

            // Add to current batch
            this.addToIndexMap(currentClasses, indexEntry);
            this.addToIndexMap(finalIndex, indexEntry);
            
            currentClassCount++;
            totalClassCount++;
            
            // Save current batch if it's getting too large
            if (currentClassCount >= this.MAX_CLASSES_PER_FILE) {
              const indexFilePath = this.getIndexFilePath(repositoryPath, currentIndexFile);
              await this.saveIndexFile(indexFilePath, currentClasses);
              metadata.indexFiles.push(path.basename(indexFilePath));
              
              // Clear current batch
              currentClasses.clear();
              currentClassCount = 0;
              currentIndexFile++;
              
              // Force garbage collection
              if (global.gc) {
                global.gc();
              }
            }
          }
          
        } catch (error) {
          console.error(`[StreamingIndexManager] 处理artifact失败 ${artifact.artifactId}: ${error}`);
          continue;
        }
      }
      
      // Force garbage collection after each batch
      if (global.gc) {
        global.gc();
      }
    }

    // Save any remaining classes
    if (currentClassCount > 0) {
      const indexFilePath = this.getIndexFilePath(repositoryPath, currentIndexFile);
      await this.saveIndexFile(indexFilePath, currentClasses);
      metadata.indexFiles.push(path.basename(indexFilePath));
    }

    metadata.totalClasses = totalClassCount;
    await this.saveMetadata(metadata);
    
    console.error(`[StreamingIndexManager] 流式索引生成完成:`);
    console.error(`[StreamingIndexManager] - 总计 ${metadata.totalArtifacts} 个artifact`);
    console.error(`[StreamingIndexManager] - 总计 ${metadata.totalClasses} 个类`);
    console.error(`[StreamingIndexManager] - 索引文件数量: ${metadata.indexFiles.length}`);
    console.error(`[StreamingIndexManager] - 平均每文件: ${Math.round(metadata.totalClasses / metadata.indexFiles.length)} 个类`);

    return finalIndex;
  }

  private addToIndexMap(classesMap: Map<string, ClassIndexEntry[]>, indexEntry: ClassIndexEntry): void {
    // Index by simple class name
    const simpleClassName = indexEntry.className.toLowerCase();
    if (!classesMap.has(simpleClassName)) {
      classesMap.set(simpleClassName, []);
    }
    classesMap.get(simpleClassName)!.push(indexEntry);

    // Index by full class name
    const fullClassName = indexEntry.fullName.toLowerCase();
    if (fullClassName !== simpleClassName) {
      if (!classesMap.has(fullClassName)) {
        classesMap.set(fullClassName, []);
      }
      classesMap.get(fullClassName)!.push(indexEntry);
    }

    // Index by package name
    if (indexEntry.packageName) {
      const packageKey = `package:${indexEntry.packageName.toLowerCase()}`;
      if (!classesMap.has(packageKey)) {
        classesMap.set(packageKey, []);
      }
      classesMap.get(packageKey)!.push(indexEntry);
    }
  }

  searchClasses(classesMap: Map<string, ClassIndexEntry[]>, className: string): ClassIndexEntry[] {
    const searchTerm = className.toLowerCase();
    const results: ClassIndexEntry[] = [];
    const seenClasses = new Set<string>();

    console.error(`[StreamingIndexManager] 在索引中搜索类: ${className}`);

    // Direct lookup for exact matches
    if (classesMap.has(searchTerm)) {
      const exactMatches = classesMap.get(searchTerm)!;
      for (const match of exactMatches) {
        const key = `${match.fullName}:${match.artifact.localPath}`;
        if (!seenClasses.has(key)) {
          results.push(match);
          seenClasses.add(key);
        }
      }
    }

    // Partial matching - search through keys
    for (const [key, entries] of classesMap.entries()) {
      if (key.includes(searchTerm) && !key.startsWith('package:')) {
        for (const entry of entries) {
          const entryKey = `${entry.fullName}:${entry.artifact.localPath}`;
          if (!seenClasses.has(entryKey)) {
            if (entry.className.toLowerCase().includes(searchTerm) || 
                entry.fullName.toLowerCase().includes(searchTerm)) {
              results.push(entry);
              seenClasses.add(entryKey);
            }
          }
        }
      }
    }

    console.error(`[StreamingIndexManager] 搜索完成，找到 ${results.length} 个匹配的类`);
    return results;
  }

  async deleteIndex(repositoryPath: string): Promise<void> {
    try {
      const metadataPath = this.getMetadataFilePath(repositoryPath);
      
      // Load metadata to find all index files
      try {
        const metadataData = await fs.readFile(metadataPath, 'utf-8');
        const metadata: RepositoryIndexMetadata = JSON.parse(metadataData);
        
        // Delete all index files
        for (const indexFile of metadata.indexFiles) {
          const indexPath = path.join(this.indexesDir, indexFile);
          try {
            await fs.unlink(indexPath);
          } catch (error) {
            console.error(`[StreamingIndexManager] 删除索引文件失败 ${indexFile}: ${error}`);
          }
        }
      } catch (error) {
        console.error(`[StreamingIndexManager] 读取元数据失败: ${error}`);
      }
      
      // Delete metadata file
      await fs.unlink(metadataPath);
      console.error(`[StreamingIndexManager] 已删除索引: ${path.basename(metadataPath)}`);
      
    } catch (error) {
      console.error(`[StreamingIndexManager] 删除索引失败: ${error}`);
    }
  }

  async getIndexStats(repositoryPath: string): Promise<any> {
    try {
      const metadataPath = this.getMetadataFilePath(repositoryPath);
      const metadataData = await fs.readFile(metadataPath, 'utf-8');
      const metadata: RepositoryIndexMetadata = JSON.parse(metadataData);
      
      let totalSize = 0;
      for (const indexFile of metadata.indexFiles) {
        try {
          const indexPath = path.join(this.indexesDir, indexFile);
          const stats = await fs.stat(indexPath);
          totalSize += stats.size;
        } catch (error) {
          // Ignore individual file errors
        }
      }
      
      return {
        version: metadata.version,
        repositoryPath: metadata.repositoryPath,
        generatedAt: new Date(metadata.generatedAt).toISOString(),
        lastScanned: new Date(metadata.lastScanned).toISOString(),
        totalArtifacts: metadata.totalArtifacts,
        totalClasses: metadata.totalClasses,
        indexFiles: metadata.indexFiles.length,
        sizeOnDisk: totalSize,
        avgClassesPerFile: Math.round(metadata.totalClasses / metadata.indexFiles.length)
      };
    } catch (error) {
      return null;
    }
  }
}